/**
 * @file swei_cache.c
 * @brief 
 * @author henry.cai (henry.cai@queclink.com)
 * @version 1.0
 * @date 2024-09-10
 * 
 * @copyright Copyright (C) 2019 Queclink Wireless Solutions (ShenZhen) Co., Ltd. All Rights Reserved.
 * 
 * @par 修改日志:
 * <table>
 * <tr><th>Date       <th>Version <th>Author  <th>Description
 * <tr><td>2024-09-10 <td>1.0     <td>henry.cai     <td>缓存队列，盲区数据，定期存入flash，关机前写入flash，开机时从flash中读出
 * </table>
 */
#include "include/port/swei_log.h"

#include "include/swei_common.h"
#include "include/swei_cache.h"
#include "include/swei_dbuff.h"
#include "include/swei_blinddata.h"

#include "global.h"
#include "FreeRTOS.h"
#include "queue.h"




cache_record_t cache_rec = {0};


/********************************* loc cache ****************************************/
#define CACHE_MAX_SIZE (128)
QueueHandle_t cache_queue = NULL;
static uint32_t cache_id = 0;


int32_t flash2cache(uint8_t *flash_data, uint16_t flash_len, swei_datatype_t *dat);

/// @brief 
/// @param dat 
/// @return 
int32_t swei_cache_peek(swei_datatype_t *dat)
{
    // 1. 队列创建
    if (NULL == cache_queue) {
        cache_queue = xQueueCreate(CACHE_MAX_SIZE, sizeof(swei_datatype_t));
    }

    // 2. 队列有效性检查
    if (NULL == cache_queue) {
        log_e("cache queue have not been created!\r\n");
        return -EIO;
    }

    if (swei_cache_is_empty())
        return -EIO;

    if (pdPASS != xQueuePeek(cache_queue, dat, 0)) {
        return -EIO;
    }

    return SUCC;
}
/// @brief 待发送消息存入发送缓存区
/// @param dat 待发送消息数据结构
/// @return 
int32_t swei_cache_push(swei_datatype_t *dat, uint8_t insert_type)
{
    // 1. 队列创建
    if(NULL == cache_queue)
    {
        cache_queue = xQueueCreate(CACHE_MAX_SIZE, sizeof(swei_datatype_t));
    }
    
    // 2. 队列有效性检查
    if(NULL == cache_queue)
    {
        log_e("cache queue have not been created!\r\n");
        return -EIO;
    }

    // 3. 消息入队列
    dat->data_id = cache_id + 1;
    cache_id += 1;
    cache_id %= 0xFFFFFFFF;

    if(insert_type == INSERT_FRONT) {
        if(pdPASS != xQueueSendToFront(cache_queue, dat, 0)) {
            return -EIO;
        }
    } else {
        if(pdPASS != xQueueSendToBack(cache_queue, dat, 0)) {
            return -EIO;
        }
    }

    cache_rec.in_cnt++;

    return SUCC;
}

/// @brief 从buffer中取出dat节点，常规dat节点应该是在队列首部，
///        但是考虑队列满后最后消息被出队的情况，优先pick后id相同再出队
/// @param dat 
/// @return 
int32_t swei_cache_pop(swei_datatype_t *dat, uint8_t is_match)
{
    swei_datatype_t temp = {0};

    // 1. 队列创建
    if (NULL == cache_queue) {
        cache_queue = xQueueCreate(CACHE_MAX_SIZE, sizeof(swei_datatype_t));
    }

    // 2. 队列有效性检查
    if (NULL == cache_queue || NULL == dat) {
        log_e("cache queue or dat have not been created!\r\n");
        return -EIO;
    }

    // 3. 消息出队列
    if (is_match) {
        // 3.1 消息匹配后出队
        if (pdPASS != xQueuePeek(cache_queue, &temp, 0)) {
            return -EIO;
        }

        if (dat->data_id != temp.data_id)
            return -EPERM;
    }

    if (pdPASS != xQueueReceive(cache_queue, dat, 0)) {
        return -EIO;
    }
    cache_rec.out_cnt++;

    return SUCC;
}


/// @brief 获取buffer状态
/// @param  
/// @return 
bool swei_cache_is_empty(void)
{
    if(NULL == cache_queue)
    {
        return true;
    }

    if(pdFAIL == uxQueueMessagesWaiting(cache_queue))
        return true;

    return false;
}


/// @brief 获取buffer状态
/// @param  
/// @return 
bool swei_cache_is_full(void)
{
    if(NULL == cache_queue)
    {
        return false;
    }

    if(pdFAIL == uxQueueSpacesAvailable(cache_queue))
        return true;

    return false;
}

/**
 * @brief cache写入flash
 */
void swei_cache_flush(void)
{
    swei_datatype_t dat;

    if (NULL == cache_queue) {
        return;
    }

    // 循环读取cache存入flash环形队列，类似其他项目的record
    while (SUCC == swei_cache_peek(&dat)) {
        if (dat.data_type == TYPE_LOC || dat.data_type == TYPE_ALARM) {
            // 1. 位置数据告警数据存入盲区
            swei_cache2blinddata(&dat);
        } else {
            // 2. 其他数据存入dbuff
            swei_dbuff_push(&dat);
        }

        swei_cache_pop(&dat, true);
    }
}

/**
 * @brief flash加载到cache
 */
void swei_cache_load(void)
{
    swei_datatype_t dat;
    uint8_t flash_data[512] = {0};
    uint32_t flash_len      = 0;

    if (NULL == cache_queue) {
        cache_queue = xQueueCreate(CACHE_MAX_SIZE, sizeof(swei_datatype_t));
    }

    // 1. flash blinddata读出到cache
    while ((flash_len = swei_blinddata_peek(flash_data, sizeof(flash_data))) > 0) {
        // log_i("blind data peek out, data len = %d\r\n", flash_len);

        // 1. flash数据转化为cache数据结构
        if (0 == flash2cache(flash_data, flash_len, &dat)) {
            break;
        }

        // 2. 数据入cache
        if (dat.data_len && dat.data_ptr) {
            swei_cache_push(&dat, INSERT_BACK);
            swei_blinddata_pop(flash_data, sizeof(flash_data));
        }

        // 3. 队列满后不再读取
        if (swei_cache_is_full()) {
            log_w("cache is full, stop load blind data!");
            break;
        }
    }

    // 2. 队列满后不再读取
    if (swei_cache_is_full()) {
        log_w("cache is full, stop load dbuff data!");
        return;
    }

    // 3. ram dbuff 读出到 cache
    while (SUCC == swei_dbuff_peek(&dat)) {
        log_i("dbuff data peek out, data len = %d\r\n", flash_len);

        // 3.1 数据入缓存
        if (dat.data_len && dat.data_ptr) {
            swei_cache_push(&dat, INSERT_BACK);
            swei_dbuff_pop(&dat, true);
        }

        // 3.2. 队列满后不再读取
        if (swei_cache_is_full()) {
            log_w("cache is full, stop load dbuff data!");
            break;
        }
    }
}

/**
 * 将闪存数据转换为缓存数据
 * 
 * 该函数的主要作用是将存储在闪存中的数据转换到缓存中，并分配必要的内存空间
 * 对于不完整或者损坏的数据，函数将返回0，以指示转换过程中的问题
 * 
 * @param flash_data  指向闪存数据的指针
 * @param flash_len   闪存数据的长度
 * @param dat         指向缓存数据结构的指针，用于存储转换后的数据
 * 
 * @return 返回转换后数据的长度如果转换失败或者参数无效，则返回0
 */
int32_t flash2cache(uint8_t *flash_data, uint16_t flash_len, swei_datatype_t *dat)
{
    swei_datatype_t *temp = (swei_datatype_t *)flash_data;

    if (!dat || !flash_data)
        return 0;

    if (temp->vaild != 0xDEADBEEF) {
        return 0;
    }
    
    dat->data_ptr = qlmalloc(temp->data_len);
    dat->data_len = 0;
    if(dat->data_ptr)
    {
        memset(dat->data_ptr, 0, temp->data_len);
        memcpy(dat->data_ptr, flash_data + sizeof(swei_datatype_t) - 4, temp->data_len);
        dat->data_len   = temp->data_len;
        dat->data_id    = temp->data_id;
        dat->data_type  = temp->data_type;
    }

    return dat->data_len;
}

/**
 * @brief 获取记录的信息
 * @return cache_record_t* 
 */
cache_record_t *swei_cache_get_rec(void)
{
    return &cache_rec;
}

/**
 * @brief 获取记录的数量
 * @return
 */
int32_t swei_cache_item_get(void)
{
    int item = 0;
    if (NULL != cache_queue)
        item = uxQueueMessagesWaiting(cache_queue);

    return item;
}
/**
 * @brief 清空cache所有数据
 * @return cache_record_t* 
 */
void swei_cache_clear(void)
{
    swei_datatype_t dat = {0};
    
    if (NULL != cache_queue) {
        taskENTER_CRITICAL();
        while(pdPASS == xQueueReceive(cache_queue, &dat, 0));
        memset(&cache_rec, 0, sizeof(cache_record_t));
        taskEXIT_CRITICAL();
    }
}
/********************************* loc cache ****************************************/

/********************************* base cache ****************************************/
#define BASE_CACHE_MAX_SIZE (128)
QueueHandle_t base_cache_queue = NULL;


/// @brief 待发送消息存入发送缓存区*
/// @param dat 待发送消息数据结构
/// @return 
int32_t swei_base_cache_push(swei_datatype_t *dat)
{
    // 1. 队列创建
    if (NULL == base_cache_queue) {
        base_cache_queue = xQueueCreate(BASE_CACHE_MAX_SIZE, sizeof(swei_datatype_t));
        // 2. 队列有效性检查
        if (NULL == base_cache_queue) {
            log_e("cache queue have not been created!\r\n");
            return -EIO;
        }
    }

    // 3. 消息入队列
    dat->data_id = cache_id + 1;
    cache_id += 1;
    cache_id %= 0xFFFFFFFF;

    if (pdPASS != xQueueSendToBack(base_cache_queue, dat, 0)) {
        return -EIO;
    }

    cache_rec.in_cnt++;

    return SUCC;
}

/// @brief 从buffer中取出dat节点，常规dat节点应该是在队列首部，
///        但是考虑队列满后最后消息被出队的情况，优先pick后id相同再出队
/// @param dat 
/// @return 
int32_t swei_base_cache_pop(swei_datatype_t *dat)
{
    // 1. 队列创建
    if (NULL == base_cache_queue) {
        base_cache_queue = xQueueCreate(BASE_CACHE_MAX_SIZE, sizeof(swei_datatype_t));
         // 2. 队列有效性检查
        if (NULL == base_cache_queue || NULL == dat) {
            log_e("cache queue or dat have not been created!\r\n");
            return -EIO;
        }
    }

   
    // 3. 消息出队列
    if (pdPASS != xQueueReceive(base_cache_queue, dat, 0)) {
        return -EIO;
    }

    cache_rec.out_cnt++;

    return SUCC;
}

/********************************* base cache ****************************************/